/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.kweative.model.apt;

import com.kweative.model.apt.annotation.TableDeclaration;
import com.sun.mirror.type.TypeVariable;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Type.ClassType;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 *
 * @author henrik
 */
@SupportedAnnotationTypes("com.kweative.model.apt.annotation.TableDeclaration")
@SupportedSourceVersion(SourceVersion.RELEASE_6)

public class TableProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(TableDeclaration.class)) {
            if (e.getKind() != ElementKind.CLASS) {
                processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.WARNING,
                    "Not a class", e);
                continue;
            }
            
            Types types = processingEnv.getTypeUtils();
            Elements elements = processingEnv.getElementUtils();
            TypeMirror column = types.erasure(elements.getTypeElement("com.kweative.model.column.Column").asType());
            TypeMirror table = types.erasure(elements.getTypeElement("com.kweative.model.Table").asType());
            TypeMirror index = types.erasure(elements.getTypeElement("com.kweative.model.Index").asType());
            TypeMirror relation = types.erasure(elements.getTypeElement("com.kweative.model.relation.Relation").asType());
            TypeMirror o2m = types.erasure(elements.getTypeElement("com.kweative.model.relation.RelationOne2Many").asType());
            TypeMirror o2o = types.erasure(elements.getTypeElement("com.kweative.model.relation.RelationOne2One").asType());

            
            TypeElement clazz = (TypeElement) e;
            String packageName = clazz.getEnclosingElement().asType().toString();
            String rowClassName = String.format("%sRow",clazz.getSimpleName());
            String rowExtendName = String.format("com.kweative.model.data.Row<%s>",clazz.getQualifiedName());
            ClassDefinition rowClass = new ClassDefinition(packageName,rowClassName);
            rowClass.setSuperClass(rowExtendName);

            processingEnv.getMessager().printMessage(
                    Diagnostic.Kind.NOTE,
                    "Generating " + rowClass.getFQN());

            List<VariableElement> fields = ElementFilter.fieldsIn(elements.getAllMembers(clazz));
            for(VariableElement field: fields) {
                if (!field.getEnclosingElement().equals(clazz)) continue;
                
                if (types.isSubtype(field.asType(),column)) {
                    TypeElement fieldElm = (TypeElement)types.asElement(field.asType());
                    List<ExecutableElement> methods = ElementFilter.methodsIn(elements.getAllMembers(fieldElm));
                    for(ExecutableElement method: methods) {
                        if (method.getSimpleName().contentEquals("unserialize")) {
                            //System.out.println(method);
                            TypeMirror returnType = method.getReturnType();
                            //System.out.println(returnType.toString() + " " + field.getSimpleName());
                            rowClass.addField(returnType.toString(),field.getSimpleName().toString());
                        }
                    }
                } else if (types.isSubtype(field.asType(),o2o)) {
                    ClassType type = (ClassType) field.asType();
                    String returnType = "";

                    com.sun.tools.javac.util.List<Type> typeArguments = type.getTypeArguments();
                    if (typeArguments.isEmpty())
                        returnType = rowExtendName;
                    else {
                        returnType = String.format("%sRow",typeArguments.get(0).toString());
                    }
                    
                    MethodDefinition getter = rowClass.addMethod(String.format("get%s",field.getSimpleName()),returnType);
                    getter.setBody(new String[]{
                        String.format("return (%s) getByRelation(getTable().%s);",returnType,field.getSimpleName())
                    });
					
                    rowClass.addField(returnType,field.getSimpleName().toString());
                }
            }
            try {
                JavaFileObject file = processingEnv.getFiler().createSourceFile(rowClass.getFQN());
				
                Writer w = file.openWriter();
                try {
                    w.write(rowClass.toString());
					//System.out.println(rowClass.toString());
                } finally {
                    w.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(TableProcessor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return true;
    }

}
